#include <ESP8266WiFi.h>      // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h> //  ESP8266WiFiMulti库
#include <ESP8266WebServer.h> //  ESP8266WebServer库
#include <ArduinoJson.h>      //  ArduinoJson库

#define buttonPin D3 // 按钮引脚D3

ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'

ESP8266WebServer esp8266_server(80); // 建立网络服务器对象，该对象用于响应HTTP请求。监听端口（80）

IPAddress local_IP(192, 168, 1, 123); // 设置ESP8266-NodeMCU联网后的IP
IPAddress gateway(192, 168, 1, 1);    // 设置网关IP（通常网关IP是WiFI路由IP）
IPAddress subnet(255, 255, 255, 0);   // 设置子网掩码
IPAddress dns(192, 168, 1, 1);        // 设置局域网DNS的IP（通常局域网DNS的IP是WiFI路由IP）

void setup()
{
    Serial.begin(9600); // 启动串口通讯
    Serial.println("");

    // 将引脚设置为输入上拉模式
    pinMode(D1, INPUT_PULLUP);
    pinMode(D2, INPUT_PULLUP);
    pinMode(buttonPin, INPUT_PULLUP); // NodeMCU开发板按键连接在D3引脚上

    // 设置开发板网络环境
    if (!WiFi.config(local_IP, gateway, subnet))
    {
        Serial.println("Failed to Config ESP8266 IP");
    }

    //通过addAp函数存储  WiFi名称       WiFi密码
    wifiMulti.addAP("301fang", "lyq1988w112233");

    int i = 0;
    while (wifiMulti.run() != WL_CONNECTED)
    {                // 此处的wifiMulti.run()是重点。通过wifiMulti.run()，NodeMCU将会在当前
        delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
        Serial.print(i++);
        Serial.print(' '); // 将会连接信号最强的那一个WiFi信号。
    }                      // 一旦连接WiFI成功，wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                           // 此处while循环判断是否跳出循环的条件。
    // WiFi连接成功后将通过串口监视器输出连接成功信息
    Serial.println('\n');           // WiFi连接成功后
    Serial.print("Connected to ");  // NodeMCU将通过串口监视器输出。
    Serial.println(WiFi.SSID());    // 连接的WiFI名称
    Serial.print("IP address:\t");  // 以及
    Serial.println(WiFi.localIP()); // NodeMCU的IP地址

    // 重点一： 建立回调函数以满足客户端的不同请求
    esp8266_server.begin();
    esp8266_server.on("/", handleRoot);
    esp8266_server.on("/info", handleInfo);
    esp8266_server.on("/digital_pin", handleDigitalPin);

    Serial.println("HTTP esp8266_server started"); //  告知用户ESP8266网络服务功能已经启动
}

void loop()
{
    // 处理http服务器访问
    esp8266_server.handleClient();
}

void handleRoot()
{ //处理网站目录“/”的访问请求
    esp8266_server.send(200, "application/json", rootJson());
}

void handleInfo()
{ //处理网站目录“/info”的访问请求
    esp8266_server.send(200, "application/json", infoJson());
}

void handleDigitalPin()
{ //处理网站目录“/digital_pin”的访问请求
    esp8266_server.send(200, "application/json", digitalPinJson());
}

// 实时获取ESP8266开发板引脚信息并且建立JSON信息
// 以便ESP8266服务器通过响应信息发送给客户端
String rootJson()
{
    // 开始ArduinoJson Assistant的serialize代码
    const size_t capacity = JSON_OBJECT_SIZE(1) + 3 * JSON_OBJECT_SIZE(3);
    DynamicJsonDocument doc(capacity);

    JsonObject info = doc.createNestedObject("info");
    info["name"] = "taichimaker";
    info["url"] = "www.taichi-maker.com";
    info["email"] = "taichimaker@163.com";

    JsonObject digital_pin = doc.createNestedObject("digital_pin");
    digital_pin["d1"] = String(digitalRead(D1));
    digital_pin["d2"] = String(digitalRead(D2));
    digital_pin["d3"] = String(digitalRead(D3));

    JsonObject analog_pin = doc.createNestedObject("analog_pin");
    analog_pin["a0"] = String(analogRead(A0));
    // 结束assistant的serialize代码

    String jsonCode;
    serializeJson(doc, jsonCode);
    Serial.print("Root Json Code: ");
    Serial.println(jsonCode);

    return jsonCode;
}

//建立infoJson信息
String infoJson()
{

    // 开始ArduinoJson Assistant的serialize代码
    const size_t capacity = JSON_OBJECT_SIZE(1) + 3 * JSON_OBJECT_SIZE(3) + 140;
    DynamicJsonDocument doc(capacity);

    JsonObject info = doc.createNestedObject("info");
    info["name"] = "taichimaker";
    info["url"] = "www.taichi-maker.com";
    info["email"] = "taichimaker@163.com";
    // 结束assistant的serialize代码

    String jsonCode;
    serializeJson(doc, jsonCode);
    Serial.print("info Json Code: ");
    Serial.println(jsonCode);

    return jsonCode;
}

//建立digitalPinJson信息
String digitalPinJson()
{

    // 开始ArduinoJson Assistant的serialize代码
    const size_t capacity = JSON_OBJECT_SIZE(1) + 3 * JSON_OBJECT_SIZE(3) + 140;
    DynamicJsonDocument doc(capacity);

    JsonObject digital_pin = doc.createNestedObject("digital_pin");
    digital_pin["d1"] = String(digitalRead(D1));
    digital_pin["d2"] = String(digitalRead(D2));
    digital_pin["d3"] = String(digitalRead(D3));

    String jsonCode;
    serializeJson(doc, jsonCode);
    Serial.print("root json Code: ");
    Serial.println(jsonCode);

    return jsonCode;
}